function isNumeric(c) {
  return c >= "0" && c <= "9";
}

export function lexer(file, str) {
  let line = 1;
  let column = 1;
  let cursor = 0;
  let char = str[cursor];

  // console.log("lexer:", char);
  const next = () => {
    cursor++;
    char = str[cursor];
    // console.log("lexer:", char);
    column++;
  };

  const newline = () => {
    line++;
    column = 1;
  };

  const number = () => {
    let buffer = "";
    const start = { line, column };
    while (isNumeric(char)) {
      // TODO 符合的语法组合起来
      buffer += char;

      next();
    }

    if (buffer.length >= 1) {
      const end = { line, column };
      return {
        type: "NumbericLiteral",
        value: Number(buffer),
        loc: {
          file,
          start,
          end,
        },
      };
    }

    return null;
  };

  const regexp = () => {
    // 正则的斜杠
    if (char === "/") {
      const start = { line, column };
      next();
      if (char === "/") {
        return readComment(start);
      }
      next();
      next();
      next();
      next();
      const end = { line, column };
      return {
        type: "RegExpToken",
        // value: "/",
        loc: {
          file,
          start,
          end,
        },
      };
    }
  }

  //#region // 注释
  const readComment = (start) => {

    for (;;) {
      if (char === "\n") {
        newline();
        next();
        break;
      }

      if (char === undefined) {
        break;
      }

      next();
    }

    // while (char !== "\n" && char !== undefined) {
    //   next();  
    //   newline();
    // }
    
    const end = { line, column };

    return {
      type: "CommentToken",
      // value: "//", // 注释
      loc: {
        file,
        start,
        end,
      },
    };
  }
  //#endregion

  const operator = () => {
    if (char === "+") {
      const start = { line, column };
      next();
      const end = { line, column };
      return {
        type: "PlusToken",
        // value: "+",
        loc: {
          file,
          start,
          end,
        },
      };
    }
    if (char === "*") {
      const start = { line, column };
      next();
      const end = { line, column };
      return {
        type: "MulToken",
        // value: "*",
        loc: {
          file,
          start,
          end,
        },
      };
    }
    if (char === "/") {
      const start = { line, column };
      next();
      if (char === "/") {
        next();
        return readComment(start);
      }
      const end = { line, column };
      return {
        type: "DivToken",
        // value: "/",
        loc: {
          file,
          start,
          end,
        },
      };
    }

    return null;
  };

  const isWhitespace = (c) => {
    return c === " " || c === "\t";
  };

  const whitespace = () => {
    if (isWhitespace(char)) {
      next();
    } else {
      return null;
    }

    // while (isWhitespace(char)) {
    //   next();
    // }

    return true;
  };

  const eol = () => {
    if (char === "\n") {
      next();
      newline();
    } else {
      return null;
    }

    // while (char === '\n') {
    //   next();
    //   newline();
    // }

    return true;
  };

  const eof = () => {
    if (char === undefined) {
      const start = { line, column };
      const end = start;
      return {
        type: "EndOfFileToken",
        // value: undefined
        loc: {
          file,
          start,
          end,
        },
      };
    }
    return null;
  };

  const next2 = (mode) => {
    for(;;){
      const token = mode === "expression"
        ? whitespace() || regexp() || number() || eol()
        : whitespace() || operator() || number() || eol();

      if (token) {
        if (token === true) {
          continue;
        }

        return token;
      }

      const maybeEof = eof();
      if (maybeEof) {
        return maybeEof;
      }

      // 找到不符合的语法
      // throw new SyntaxError(`Unexpected character "${char}" at ${cursor + 1}`);
      throw new SyntaxError(
        `Unexpected character "${char}" at ${file} ${line}:${column}`
      );
    }
  }

  return {
    next: next2
  }
}
